from abc import ABC
import os
from tornado.web import RequestHandler

from Server.Utils import media, network, fileType, htmlUtils, htmlTemplate
from Server import var, const


class Param:
    EnableFileUpload: bool = True
    ServerConfig = {}
    RoutePath = {}
    ProxyUrl = '/'
    HiddenConfig = {}


def initHandlerParam(config: dict):
    fileType.TypeListDict = config['file_type']
    Param.HiddenConfig = config['hidden']
    Param.RoutePath['/'] = config['route_path']['root']['path']
    Param.ServerConfig = config['server']
    for sub in config['route_path']['sub']:
        Param.RoutePath[sub['route']] = sub['path']
    Param.ProxyUrl = var.PROXY_URL
    fileType.TypeListDict['image'] = config['file_type']['image']
    fileType.TypeListDict['audio'] = config['file_type']['audio']
    fileType.TypeListDict['other'] = config['file_type']['other']


def uri2localPath(uri: str, proxyUrl: str, routePath: dict) -> str:
    """
    uri 转本地路径

    :param uri:
    :param proxyUrl:
    :param routePath:
    :return:
    """
    if proxyUrl[-1] == '/':
        proxyUrl = proxyUrl[:-1]
    uri = network.unquoteUrlCode(uri)
    uri = uri[len(proxyUrl):]
    for route in routePath.keys():
        if uri.startswith(route):
            if routePath[route] == '$':
                continue
            path = uri.replace(route, routePath[route])
            if os.path.exists(path):
                return path
    return ''


class FileServerRequestHandler(RequestHandler, ABC):
    def set_default_headers(self) -> None:
        self.set_header('Server', 'File Server/' + const.VERSION)

    def setContentType(self, value: str):
        self.set_header('Content-Type', value)

    def real_ip(self):
        if Param.ServerConfig['proxy_real_ip_header'] in self.request.headers.keys():
            return self.request.headers[Param.ServerConfig['proxy_real_ip_header']]
        else:
            return self.request.remote_ip

    def getRfAndRoute(self, url: str = '') -> tuple:
        """
        获取 route 和 routeFrom，route 是完整路由（纯路径，不含参数），routeFrom 是来自路由表的哪个路由
        :param url:
        :return:
        """
        route = network.unquoteUrlCode(self.request.uri.split('?')[0])
        routeFrom = route[:-1 * (1 + len(url))]
        if routeFrom == '':
            routeFrom = '/'
        if route[-1] == '/' and route != '/':
            route = route[:-1]
        return routeFrom, route

    def getArgumentWithDefaultAndType(self, name, defaultValue, valueType: str):
        args = self.request.arguments
        if name not in args:
            return defaultValue
        value = self.get_argument(name)
        try:
            ret = eval(valueType)(value)
        except ValueError:
            ret = defaultValue
        return ret

    def setDownloadAction(self, filename: str):
        """
        通知浏览器下载文件

        :param filename:
        :return:
        """
        filename = fileType.getFileName(filename)
        # http 头只支持 ASCII 字符！！！
        self.set_header('Content-Disposition', 'attachment; filename="{0}"'.format(network.quoteUrlCode(filename)))

    def needHide(self, path) -> bool:
        """
        判断路径是否需要隐藏

        :param path:
        :return:
        """
        if fileType.isHiddenPath(path, Param.HiddenConfig):
            if 'Referer' not in self.request.headers:
                return True
        return False

    def handlePathNotExists(self, path: str):
        """
        处理路径不存在

        :param path: 文件或目录的路径
        :return:
        """
        # 获取文件类型
        ft = fileType.getFileType(path)
        self.set_status(404)
        if ft == fileType.Type.IMAGE:
            self.write(media.read404image())
            self.setContentType('image/jpg')
        else:
            self.write(htmlTemplate.NotFound)
            self.setContentType('text/html')

    def handleHiddenPath(self, path: str):
        """
        处理隐藏路径

        :param path: 文件或目录的路径
        :return:
        """
        self.responseCodeHtml(404)

    def handleMakeDirHtml(self, dirPath: str, route: str, routeFrom: str):
        """
        处理目录构建HTML（目录必须存在）

        :param dirPath:
        :param route:
        :param routeFrom:
        :return:
        """
        self.write(htmlUtils.makeDirHtml(self, dirPath, Param.HiddenConfig, route, Param.ProxyUrl, routeFrom))
        self.setContentType('text/html')

    def handleReadFile(self, filePath: str, contentType: str = '', tl: bool = False, dl: bool = False):
        """
        处理文件读取（文件必须存在）

        :param dl: 是否通知浏览器下载
        :param filePath:
        :param contentType:
        :param tl: 是否翻译
        :return:
        """
        if not contentType:
            contentType = fileType.getContentType(filePath)
        # print(contentType)
        try:
            content = media.readFile(filePath)
            if tl:
                content = var.translate(content.decode())
            self.write(content)
            self.setContentType(contentType)
            # if dl:
            #     self.setDownloadAction(filePath)
        except PermissionError:  # 无访问权限的
            self.responseCodeHtml(403)

    def responseCodeHtml(self, code: int):
        self.set_status(code)
        text = ''
        if code == 400:
            text = htmlTemplate.BadRequest
        elif code == 403:
            text = htmlTemplate.Forbidden
        elif code == 404:
            text = htmlTemplate.NotFound
        self.write(text)
        self.setContentType('text/html')

    def writeFormat(self, res: bool, msg: str, **kwargs):
        resp = {
            'res': res,
            'msg': msg
        }
        for key in kwargs:
            resp[key] = kwargs[key]
        self.write(resp)
